-
Couldn't load subscription status.
- Fork 0
Version 0.2.0 #467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Version 0.2.0 #467
Conversation
Add comprehensive OAuth2/OIDC authentication support using oauth2-proxy and GitLab as identity provider, alongside existing bearer token auth. Core Changes: - Implement hybrid authentication system supporting dev/oauth/bearer modes - Add OAuth2-proxy integration with Traefik reverse proxy setup - Update frontend to detect and adapt to different authentication modes - Extend API middleware to handle OAuth headers and session management New Features: - Development mode: No authentication for local development - OAuth mode: GitLab OIDC via oauth2-proxy with cookie-based sessions - Bearer mode: Traditional API token authentication (existing behavior) - Complete Docker Compose development environment with profiles - Frontend auth mode detection with appropriate UI changes Configuration: - Add AuthMode enum with dev/oauth/bearer variants in scotty-core - Extend ApiServer settings with OAuth redirect URL and dev user config - Update CurrentUser struct to include email, name, and optional access token - Add oauth2-proxy configuration with GitLab OIDC integration Development Setup: - Docker Compose profiles for different authentication modes - Helper scripts for easy local development and testing - Complete OAuth development example in examples/oauth2-proxy/ - 1Password integration for secure secret management Documentation: - Comprehensive CLAUDE.md with architecture and development guidance - OAuth testing documentation in LOCAL_OAUTH_TESTING.md - Configuration examples for all authentication modes Testing: - Full OAuth flow validation with Playwright browser testing - Traefik service discovery and routing verification - End-to-end authentication testing across all modes This implementation provides a production-ready OAuth authentication system while maintaining backward compatibility with existing bearer token authentication.
BREAKING CHANGE: API endpoints restructured with protected resources moved to /api/v1/authenticated/* namespace Enable secure GitLab OIDC authentication for Scotty's SPA interface while maintaining CLI bearer token support. This production-ready implementation replaces manual token entry with seamless OAuth flow using oauth2-proxy v7.6.0 and Traefik ForwardAuth middleware. Key improvements: - Hybrid authentication modes: dev, oauth, and bearer for different use cases - GitLab OIDC integration supporting both gitlab.com and self-hosted instances - Redis-backed session storage eliminating 4KB cookie size limitations - Clean API separation: public endpoints at /api/v1/ and authenticated at /api/v1/authenticated/* - Reusable ForwardAuth middleware for protecting additional Docker applications - Production-ready Docker Compose setup with Traefik reverse proxy Technical implementation: - ForwardAuth pattern validates sessions and injects user headers into backend requests - Breaking change: moved protected API endpoints to dedicated authenticated namespace - Frontend auto-detects authentication mode and handles OAuth redirects seamlessly - CLI tools updated to use new authenticated API namespace - Comprehensive example setups for both development and production OAuth scenarios This enables self-service user authentication without manual token management while preserving existing CLI workflows and adding enterprise-grade session management.
- Add #[derive(Default)] to AuthMode enum as suggested by clippy - Add #[allow(dead_code)] to access_token field for future OAuth implementations - Format all code with cargo fmt All tests passing and clippy warnings resolved.
- Replace 'any' type with proper LoginResponse interface in auth flow - Add proper type annotations for API response handling - Format code to match project style guidelines
The monitorTask function was calling getTask() which only checked the local store instead of actually fetching task status from the API. Changed to use requestTaskDetails() to properly fetch task status updates, enabling the loading spinner animation when running/stopping apps.
- Remove automatic OAuth redirects from login page, require user button click - Add auth_mode to /api/v1/info endpoint for cleaner auth mode detection - Use validate-token endpoint for proper authentication checking - Remove Traefik auto-redirect middlewares that bypass login page - Consolidate OAuth/bearer redirect handling to both use /login route Improves user experience by showing login page with explicit OAuth button instead of automatic redirects that users might miss.
- Add comprehensive OAuth authentication guide with oauth2-proxy and Traefik - Remove outdated LOCAL_OAUTH_TESTING.md file - Update configuration.md with new auth_mode options and environment variables - Add oauth-authentication.md to docs navigation The new documentation covers the current OAuth implementation including: - Three authentication modes (dev, oauth, bearer) - Route-based protection (/api/v1/authenticated/*) - Working examples in examples/oauth2-proxy-oauth/ - Complete setup instructions and troubleshooting
- Reduce healthcheck interval from 30s to 10s for faster feedback - Add 15s start period to prevent failures during startup - Reduce timeout from 3s to 2s (health endpoint responds in ~20ms) - Add explicit retries=3 for clarity This improves Docker Compose startup time and provides more responsive health status feedback during development and deployment.
…tion - Implement hybrid OAuth approach (Option C) with temporary session exchange - Add OAuth session storage with 5-minute expiry for security - Create `/oauth/exchange` API endpoint for frontend token retrieval - Update OAuth callback to redirect to frontend with session ID - Separate API and frontend OAuth callback paths to prevent conflicts - Modify frontend to handle session exchange flow instead of direct tokens - Add comprehensive OAuth state management and validation - Ensure no sensitive tokens appear in URLs or browser history - Support configurable frontend callback URLs via redirect_uri parameter This approach provides secure OAuth authentication while maintaining flexibility for different deployment configurations and prevents common security issues like token exposure in URLs.
…Gravatar support This comprehensive refactoring transforms the OAuth implementation from GitLab-specific to fully OIDC-compliant and provider-agnostic, while adding modern UI enhancements. **OIDC Standards Compliance:** - Replace GitLab-specific `gitlab_url` with generic `oidc_issuer_url` configuration - Update `GitLabUser` to `OidcUser` with OIDC standard fields (`sub`, `preferred_username`) - Use OIDC `/oauth/userinfo` endpoint instead of provider-specific endpoints - Support optional OIDC claims with intelligent fallbacks **Provider Interchangeability:** - Support GitLab, Auth0, Keycloak, Google, and other OIDC providers - Provider-agnostic configuration and documentation - Remove all GitLab-specific references from codebase **Enhanced Authentication UX:** - Add reactive user store for immediate UI updates after OAuth login - Implement session exchange flow to avoid exposing tokens in URLs - Fix user info display appearing only after page reload - Add proper error handling and debugging for OAuth flows **Modern Avatar System:** - Add Gravatar support with MD5 hashing for user avatars - Implement smart fallback system: Gravatar → initials → generic avatar - Create reusable UserAvatar component with multiple sizes and shapes - Enhanced user dropdown with professional styling and logout icon **Updated Documentation:** - Comprehensive OIDC authentication guide with provider examples - Updated CLI documentation with new command structure - Provider setup instructions for GitLab, Auth0, Keycloak, and Google - Migration guide from oauth2-proxy to native OIDC implementation **Frontend Improvements:** - Reactive authentication state management with Svelte stores - DaisyUI integration for consistent avatar styling - Enhanced user interface with Gravatar images and rich user dropdowns - Improved error handling and user feedback **Backend Enhancements:** - Better OIDC user field handling with meaningful fallbacks - Improved session management and token validation - Enhanced OAuth error handling and debugging capabilities The system now works seamlessly with any OIDC-compliant provider while providing a modern, professional user experience with Gravatar support and reactive UI updates.
Complete end-to-end OAuth device flow implementation enabling CLI authentication with OIDC providers like GitLab. This adds native device flow support alongside the existing web-based OAuth flow. Key improvements: - Implement full device flow token polling and exchange in Scotty server - Add proper OIDC provider integration with device authorization grant - Fix server info endpoint to use OIDC-compliant field names - Resolve server URL mismatch between localhost and 127.0.0.1 in token storage - Update scottyctl to handle device flow authentication properly - Add comprehensive error handling for OAuth flow states Technical changes: - Server: Add exchange_device_code_for_token() method for GitLab token polling - Server: Store device flow session interval for proper polling cadence - Server: Update info handler to return oidc_issuer_url instead of gitlab_url - scottyctl: Fix token storage to use user-provided server URL - scottyctl: Remove placeholder user info and use actual token response data - scottyctl: Update OAuth structures to be fully OIDC-compliant The device flow now supports the complete OAuth 2.0 Device Authorization Grant flow (RFC 8628) with proper error handling for authorization_pending, access_denied, and expired_token scenarios. Tested with GitLab OIDC provider - full authentication and API access working.
- Remove unused variables in frontend components - Remove imageLoaded variable from user-avatar component - Remove oauthRedirectUrl variable from login page - Apply Prettier formatting across all frontend files - Fix arrow function formatting in stores and components - Improve code readability with consistent indentation These changes resolve ESLint warnings and ensure consistent code style across the frontend codebase.
# Conflicts: # Cargo.lock
Update all documentation to use correct CLI command syntax: - Change apps subcommand to app:subcommand format - Change blueprints list to blueprint:list - Change notifications add/remove to notify:add/notify:remove This aligns documentation with actual CLI implementation.
Implement complete test suite for bearer token and OAuth authentication flows: **Bearer Authentication Tests (14 tests)** - Valid/invalid/missing token authentication scenarios - Malformed headers and public endpoint access validation - Configuration-dependent behavior testing - Cross-authentication mode validation **OAuth Authentication Tests (8 tests)** - Complete OAuth device flow: authorization → token → protected endpoint access - OAuth web flow: authorization URL generation, callback handling, session exchange - Mock OAuth provider integration with exact API format matching - OAuth provider error handling and authorization pending states **Key Technical Achievements** - Tests actual Scotty application router with complete middleware stack - Uses AppState access for OAuth session store manipulation to test complete flows - Mock OAuth provider with wiremock exactly matches implementation request formats - Validates end-to-end authentication: auth flow → token → protected API access - All 22 tests validate real implementation behavior, not isolated components The AppState approach solves OAuth web flow testing complexity by directly populating session stores, enabling complete flow validation without complex callback coordination between HTTP requests. Dependencies: Add axum-test, tokio-test, wiremock for testing infrastructure.
Removed assert!(true) statements that would be optimized out by the compiler, as flagged by clippy's assertions_on_constants lint. These assertions provided no actual test value and were replaced with comments explaining the test flow.
- Add preflight version check using semver to ensure major/minor compatibility - Create shared ServerInfo and OAuthConfig types in scotty_core for consistency - Add --bypass-version-check flag for emergency situations - Update AuthMode enum to support serialization with proper defaults - Version check runs before commands requiring server connection - Show clear error messages when versions are incompatible - Skip version check for auth commands (login/logout) and completion This prevents backwards compatibility issues by ensuring scottyctl and scotty server versions are compatible before executing operations.
- Replace direct println! calls with app_context.ui() methods - Use proper success/failed status methods for better terminal integration - Reduce excessive emoji usage for cleaner, more professional output - Maintain colored text for important information (URLs, usernames, servers) - Ensure consistent UI behavior with status line management
This commit centralizes shared functionality in scotty-core and significantly improves OAuth error handling with type-safe enums. ## New shared modules in scotty-core: - Add HTTP client with retry logic and exponential backoff - Add unified OAuth types with type-safe error enums - Add version management utilities with compatibility checking - Move retry logic from scottyctl to shared location ## OAuth improvements: - Replace string literals with OAuthErrorCode enum for type safety - Add built-in error descriptions to OAuth error codes - Update all OAuth handlers to use type-safe error responses - Maintain backward compatibility with legacy error formats ## HTTP client consolidation: - Create shared HttpClient with builder pattern and timeout support - Replace scattered reqwest::Client usage with shared implementation - Add proper error handling and retry policies across the workspace - Update OAuth flows to use shared HTTP client ## Version management: - Add comprehensive version comparison and compatibility utilities - Update preflight checker to use shared version management - Add user-friendly version formatting and update recommendations - Include extensive test coverage for version handling ## Code cleanup: - Remove unused dependencies (utoipa-axum) - Fix clippy warnings and improve code consistency - Update imports to use shared types across workspace - Maintain full backward compatibility All tests pass, ensuring no regressions were introduced.
This commit consolidates the OAuth error handling across the Scotty project by: - Unifying OAuthError and OAuthErrorCode into a single comprehensive error type in scotty-core - Implementing smart IntoResponse for AppError that returns OAuth-compliant ErrorResponse format for OAuth errors - Adding proper HTTP status code mappings for all OAuth error types (400, 401, 403, 404, 429) - Fixing device flow "Server error" issue by improving scottyctl error handling to process all OAuth status codes - Adding SlowDown variant for handling OAuth2 "slow_down" error during device flow polling - Maintaining OAuth2 RFC 6749 compliance while simplifying the error architecture - Updating all components (scotty, scottyctl, scotty-core) to use the unified system with proper error conversions The device flow now properly handles polling rate limiting and provides specific error messages instead of generic "Server error" responses.
Add role-based access control (RBAC) system using Casbin for granular permission management across apps and groups. Key features: - Group-based app organization (development, staging, production, default) - Role-based permissions (viewer, operator, developer, admin) - Per-app permission checks (view, manage, shell, logs, create, destroy) - Universal default group access for all users via wildcard assignment - Authorization middleware for API endpoints - Groups list endpoint (/api/v1/authenticated/groups/list) - Seamless fallback when authorization config unavailable This enables secure multi-tenant app management while maintaining backward compatibility.
- Fix token bounds checking in basic_auth.rs to prevent panics with short tokens - Implement proper get_user_permissions method in authorization service - Update authorization middleware to extract app names from API v1 paths - Fix middleware ordering by using State extractor instead of request extensions - Update router to use from_fn_with_state for require_permission middleware These changes resolve authentication failures and "App state not found" errors in the RBAC authorization system.
- Remove references to legacy api.access_token fallback - Add migration instructions for existing bearer token installations - Update PRD to reflect completed Phase 4 enforcement - Document middleware architecture improvements - Add warnings about breaking changes for bearer token authentication The authorization system now requires explicit RBAC assignments for all bearer tokens, removing the legacy fallback behavior.
Updates scottyctl to check server auth mode before using stored OAuth tokens. When server is in bearer mode, prioritizes --access-token parameter and SCOTTY_ACCESS_TOKEN environment variable over stored OAuth credentials. Resolves authentication failures where scottyctl would attempt to use invalid OAuth tokens against servers configured for bearer authentication.
- Remove fallback authorization service - RBAC is now required - Update tests to use actual RBAC configuration instead of fallback - Add test bearer token to policy configuration - Remove obsolete test for no-token configuration - Improve log format with timestamp, level, target, and message - Clean up telemetry configuration and reduce verbose span output
…er compatibility The ts-generator was using a relative path (../frontend/src/generated) which resolved to /frontend/src/generated in Docker instead of /app/frontend/src/generated, causing the frontend-builder stage to fail with "not found" error. Now uses CARGO_MANIFEST_DIR to build an absolute path that works in both local development and Docker build contexts. - Local: /Users/stephan/dev/rust/scotty/frontend/src/generated - Docker: /app/frontend/src/generated
Pull Request Review: OAuth2/OIDC Authentication & RBAC Authorization SystemThis is an impressive and comprehensive implementation of a production-ready authentication and authorization system. The PR adds OAuth2/OIDC support with RBAC using Casbin across 100 files with 30,246 additions. Overall AssessmentSTRENGTHS:
Code Quality & Best PracticesExcellent Patterns
Security ConcernsHIGH PRIORITY - Session Storage: Recommendation: Document this limitation prominently in production deployment docs and plan for Redis/database persistence MEDIUM PRIORITY - Token Logging: Recommendation: Log token length or hash instead of partial token values MEDIUM PRIORITY - Timing Attack: Recommendation: Use constant-time comparison from the subtle crate to prevent timing attacks Test CoverageExcellent CoverageFound 7 test files with comprehensive coverage:
Test GapsMissing test scenarios:
Performance ConsiderationsGood Practices
Potential Bottlenecks
DocumentationExcellent Documentation
Documentation Needs
Recommendations SummaryMust Fix Before Merge
Should Fix Soon
Nice to Have
ConclusionThis PR represents excellent engineering work with:
The identified issues are mostly minor security hardening and operational concerns that can be addressed incrementally. The core implementation is solid. Recommendation: APPROVE with minor security fixes The authentication system is well-designed and the code quality is high. Address the timing attack and token logging concerns, and this will be production-ready. Great work! Generated by Claude Code PR Review |
Adds a SvelteKit path alias ($generated) to resolve imports from the generated TypeScript types directory. This fixes Docker build issues where Rollup couldn't resolve the relative path "../generated". Changes: - Add $generated alias in svelte.config.js pointing to src/generated - Update imports in containerLogsStore.ts to use $generated - Update imports in taskOutputStore.ts to use $generated This approach: - Works consistently in both local development and Docker builds - Follows SvelteKit conventions (similar to $lib) - Provides explicit, maintainable imports
Pull Request Review: OAuth Authentication & RBAC Authorization SystemOverviewThis is a comprehensive and well-architected implementation of OAuth/OIDC authentication and Casbin-based RBAC authorization for Scotty. The PR adds 30,249 lines across 245 files, implementing a production-ready authentication system with excellent separation of concerns. 🎯 Major ChangesAuthentication System
Authorization System
Frontend & CLI
✅ StrengthsSecurity Implementation
Code Quality
Architecture
🚨 Critical Issues1. In-Memory Session Storage (BLOCKER for Production)Severity: CRITICAL Issue: OAuth sessions stored in HashMap within single-instance memory: pub type DeviceFlowStore = Arc<Mutex<HashMap<String, DeviceFlowSession>>>;
pub type WebFlowStore = Arc<Mutex<HashMap<String, WebFlowSession>>>;
pub type OAuthSessionStore = Arc<Mutex<HashMap<String, OAuthSession>>>;Consequences:
Recommendation:
Files affected:
|
| Item | Status | Notes |
|---|---|---|
| PKCE implementation | ✅ PASS | SHA256 challenge/response |
| CSRF protection | ✅ PASS | Random tokens validated |
| Session expiration | ✅ PASS | 5-10 minute TTLs |
| Token storage | In-memory only | |
| HTTPS enforcement | Should document requirement | |
| JWT validation | Only userinfo endpoint | |
| Rate limiting | ❌ MISSING | OAuth endpoints vulnerable to abuse |
| Token rotation | No refresh token rotation | |
| Audit logging | Has logging, not structured audit log | |
| Secret management | ✅ PASS | Uses secrecy crate |
📦 Deployment Considerations
Pre-Production Checklist
- Implement Redis session storage or document single-instance limitation
- Add session cleanup mechanism
- Add rate limiting on OAuth endpoints (especially device flow polling)
- Document TLS/HTTPS requirement for OAuth callbacks
- Add health check for OIDC provider connectivity
- Test multi-instance deployment with load balancer
- Document RBAC configuration migration for existing deployments
- Verify OAuth provider setup (redirect URIs, scopes)
Breaking Changes
/api/v1/authenticated/* namespace
policy.yaml
Migration path: Well documented in authorization docs. Existing installations need to:
- Update API endpoints in clients
- Add bearer tokens to RBAC configuration
- Configure OAuth provider if using OAuth mode
🎯 Recommendations Summary
Before Merge
- Decision needed: Document single-instance limitation OR implement Redis sessions
- Add issue: Track session garbage collection implementation
- Add issue: Track JWT signature verification implementation
- Documentation: Add deployment guide with multi-instance considerations
Post-Merge (Follow-up PRs)
- Implement rate limiting on OAuth endpoints
- Add JWT signature verification for offline validation
- Implement refresh token rotation
- Add structured audit logging
- Extend test coverage per gaps above
- Add Prometheus metrics for auth/authz events
🏆 Overall Assessment
Status: ✅ APPROVE WITH RECOMMENDATIONS
This is a high-quality, well-architected implementation that follows OAuth 2.0 and OIDC best practices. The code is clean, well-tested, and properly structured. The Casbin RBAC integration is elegant and flexible.
The main concern is the in-memory session storage, which limits production deployments to single instances. This should either be:
- Implemented with Redis before merge (recommended for production-ready state)
- Clearly documented as a limitation with a follow-up issue to track
The security implementation is solid with appropriate PKCE/CSRF protections. The missing JWT verification and lack of rate limiting are acceptable for initial release if documented as known limitations.
Recommendation: Merge after deciding on session storage approach and documenting deployment constraints.
📚 Documentation Quality
✅ Excellent documentation added:
docs/content/oauth-authentication.md- Comprehensive OAuth guidedocs/content/authorization.md- RBAC configuration guideexamples/native-oauth/- Working examplesdocs/prds/authorization-system.md- Design documentation
Minor improvement: Add troubleshooting section for common OAuth issues (redirect URI mismatch, provider errors, etc.)
Great work on this implementation! The architecture is solid and the code quality is excellent. Address the session storage concern and this will be production-ready. 🚀
The alias was pointing to the directory 'src/generated' which caused Vite/Rollup to try to load the directory itself instead of the index.ts file, resulting in: "EISDIR: illegal operation on a directory, read" Now explicitly points to 'src/generated/index.ts' so Vite can resolve the module correctly.
The build script needs to run 'svelte-kit sync' to generate the .svelte-kit/tsconfig.json and other required configuration files before vite can build the project. This fixes the Docker build error where tsconfig.json couldn't find its base config: "Cannot find base config file './.svelte-kit/tsconfig.json'" The sync command generates: - .svelte-kit/tsconfig.json (base TypeScript config) - .svelte-kit/types/ (SvelteKit type definitions) - Other SvelteKit-generated files needed for the build
PR #467 Review: OAuth2/OIDC Authentication & RBAC Authorization SystemExecutive SummaryThis is an excellent and comprehensive implementation of OAuth2/OIDC authentication with Casbin-based RBAC authorization. The PR adds ~30,249 lines implementing a production-grade authentication system with: ✅ Strengths:
🎯 Overall AssessmentCode Quality: 8.5/10 - Well-structured, clean code with good patterns Recommendation: Approve with conditions - Address critical security items before production deployment. 🔒 Security ReviewCritical Issues (Must Fix Before Production)1. In-Memory Session Storage 🔴 HIGH PRIORITYLocation: pub type DeviceFlowStore = Arc<Mutex<HashMap<String, DeviceFlowSession>>>;
pub type WebFlowStore = Arc<Mutex<HashMap<String, WebFlowSession>>>;
pub type OAuthSessionStore = Arc<Mutex<HashMap<String, OAuthSession>>>;Issue: Sessions stored in memory will be lost on server restart, causing active users to lose authentication. Impact:
Recommendation: // Replace with Redis or database backend
pub trait SessionStore: Send + Sync {
async fn get(&self, key: &str) -> Option<OAuthSession>;
async fn set(&self, key: &str, value: OAuthSession, ttl: Duration);
async fn delete(&self, key: &str);
}Add feature flags for different backends:
2. Test Credentials in Production Config 🔴 HIGH PRIORITYLocation: assignments:
identifier:test-bearer-token-123:
- role: admin
scopes:
- client-a
- client-b
- qa
- defaultIssue: Hardcoded test bearer token with admin access in default production config. Files affected:
Recommendation:
// Add startup validation
fn validate_production_config(config: &AuthConfig) -> Result<()> {
const TEST_TOKENS: &[&str] = &["test-bearer-token-123", "hello-world"];
for (identifier, _) in &config.assignments {
if identifier.contains("test-") || TEST_TOKENS.contains(&identifier.as_str()) {
bail!("Production config contains test credentials: {}", identifier);
}
}
Ok(())
}3. Bearer Tokens in Plaintext 🟡 MEDIUM PRIORITYLocation: fn find_token_identifier(shared_app_state: &SharedAppState, token: &str) -> Option<String> {
for (identifier, configured_token) in &shared_app_state.settings.api.bearer_tokens {
if configured_token == token { // ⚠️ Plaintext comparison
return Some(identifier.clone());
}
}
None
}Issue: Bearer tokens stored and compared in plaintext with O(n) complexity. Recommendation: // Use bcrypt/argon2 for token hashing
use argon2::{Argon2, PasswordHash, PasswordVerifier};
pub struct TokenStore {
tokens: HashMap<String, String>, // identifier -> hashed_token
}
impl TokenStore {
pub fn verify(&self, identifier: &str, token: &str) -> bool {
if let Some(hash) = self.tokens.get(identifier) {
let parsed_hash = PasswordHash::new(hash).ok()?;
Argon2::default().verify_password(token.as_bytes(), &parsed_hash).is_ok()
} else {
false
}
}
}4. Frontend Token Storage in sessionStorage 🟡 MEDIUM PRIORITYLocation: setOAuthToken(token: string): boolean {
return this.setItem(STORAGE_KEYS.OAUTH_TOKEN, token); // ⚠️ XSS vulnerable
}Issue: OAuth tokens stored in sessionStorage are accessible to XSS attacks. Recommendation:
// Backend: Set HTTP-only cookie instead of returning token
async fn exchange_session_for_token(/* ... */) -> impl IntoResponse {
let cookie = Cookie::build("scotty_session", session_token)
.http_only(true)
.secure(true)
.same_site(SameSite::Strict)
.path("/")
.finish();
(
StatusCode::OK,
[(header::SET_COOKIE, cookie.to_string())],
Json(user_info) // Don'\''t return token
)
}5. OAuth Token Validation on Every Request 🟡 MEDIUM PRIORITYLocation: match oauth_state.client.validate_oidc_token(token).await { // ⚠️ Network call every request
Ok(oidc_user) => { /* ... */ }
}Issue: Every API request makes a network call to OIDC provider for token validation. Impact:
Recommendation: // Implement token caching with JTI tracking
pub struct TokenCache {
cache: Arc<RwLock<HashMap<String, (OidcUser, Instant)>>>,
ttl: Duration,
}
impl TokenCache {
pub async fn validate_with_cache(&self, token: &str) -> Result<OidcUser> {
// Check cache first
if let Some((user, expiry)) = self.cache.read().await.get(token) {
if Instant::now() < *expiry {
return Ok(user.clone());
}
}
// Validate with provider
let user = self.validate_oidc_token(token).await?;
// Cache result
self.cache.write().await.insert(
token.to_string(),
(user.clone(), Instant::now() + self.ttl)
);
Ok(user)
}
}Additional Security Observations✅ Good Security Practices Found:
🏗️ Architecture & Code QualityStrengths
Areas for Improvement1. Token Lookup Performance 🟡Location: // O(n) complexity - should be O(1)
for (identifier, configured_token) in &shared_app_state.settings.api.bearer_tokens {
if configured_token == token {
return Some(identifier.clone());
}
}Recommendation: // Pre-build reverse lookup map on startup
pub struct BearerTokenStore {
token_to_id: HashMap<String, String>, // token -> identifier (O(1) lookup)
}2. Authorization Middleware Ordering ✅ GOODLocation: The middleware properly checks:
Good defensive programming with early returns. 3. Global Permission Check Logic 🟡Location: pub async fn check_global_permission(&self, user: &str, action: &Permission) -> bool {
// Checks admin permissions without app context
// Could allow unintended global access
}Concern: Admin users bypass app-level checks. Verify this is intentional behavior. Recommendation: Add audit logging for global permission grants: if has_permission {
audit_log::log_global_permission_grant(user, action);
return true;
}🧪 Test CoverageExisting Tests ✅
Missing Test Coverage 🟡
Recommendation: // Add security-focused tests
#[tokio::test]
async fn test_csrf_token_mismatch() {
// Verify CSRF protection works
}
#[tokio::test]
async fn test_expired_session_rejection() {
// Test session expiration
}
#[tokio::test]
async fn test_token_replay_attack() {
// Ensure one-time use sessions
}📚 Documentation QualityExcellent Documentation ✅
Documentation Gaps 🟡
Recommendation: Add # Security Architecture
## Threat Model
- XSS attacks via token storage
- CSRF attacks on state-changing operations
- Token replay attacks
- Session hijacking
## Mitigation Strategies
- HTTP-only cookies (planned)
- CSRF tokens on all mutations
- One-time session exchange
- Short session expiration
## Production Checklist
- [ ] Remove test tokens from config
- [ ] Enable Redis session storage
- [ ] Configure CSP headers
- [ ] Set up rate limiting
- [ ] Enable audit logging🎨 Code Style & Best PracticesExcellent Patterns ✅
Minor Style Issues 🟢
🚀 Performance ConsiderationsCurrent Performance Issues 🟡
Recommendation: Benchmark and optimize: // Use RwLock instead of Mutex for read-heavy workloads
pub type DeviceFlowStore = Arc<RwLock<HashMap<String, DeviceFlowSession>>>;
// Or use concurrent hashmap
use dashmap::DashMap;
pub type DeviceFlowStore = Arc<DashMap<String, DeviceFlowSession>>;📋 Recommendations SummaryBefore Production Deployment (Critical)
Before Next Release (High Priority)
Future Improvements (Medium Priority)
Nice to Have (Low Priority)
🎉 ConclusionThis is a high-quality, well-architected implementation of OAuth2/OIDC authentication and RBAC authorization. The code demonstrates:
The main concerns are production readiness rather than fundamental design issues:
Recommendation: Merge with production deployment blockers documented. The code is production-quality with the exception of the session storage backend. I recommend:
Great work on this comprehensive feature! 🚀 📎 File ReferencesKey files for review follow-up: Authentication Core:
Authorization Core:
Frontend:
Configuration:
|
Pull Request Review: OAuth Authentication & RBAC Authorization System🎯 OverviewThis is a major feature addition implementing OAuth2/OIDC authentication and Role-Based Access Control (RBAC) authorization using Casbin. The PR is substantial (30,250 additions, 100 files) and introduces production-ready security infrastructure. Overall Assessment: ✅ Approve with Recommendations The implementation is architecturally sound with good security practices, comprehensive testing, and solid documentation. However, there are several important issues that should be addressed before production deployment. ✅ Strengths1. Comprehensive Security Implementation
2. Well-Structured Architecture
3. Excellent Test Coverage
4. Strong Documentation
🔴 Critical Issues (Must Fix)1. Hardcoded Localhost in OAuth CallbackLocation: format!(
"http://localhost:21342/oauth/callback?session_id={}",
oauth_session_id
)Issue: Hardcoded Impact: OAuth flow will fail in deployed environments, security vulnerability (HTTP). Recommendation: // Use base_url from configuration or extract from request
let base_url = state.settings.api.base_url
.as_ref()
.unwrap_or(&"http://localhost:21342".to_string());
format!("{}/oauth/callback?session_id={}", base_url, oauth_session_id)2. No Session Cleanup for Expired OAuth SessionsLocation: Issue: OAuth sessions stored in Impact: Memory leak vulnerability, especially under attack scenarios with repeated device flow polling. Recommendation: Implement background task to clean expired sessions: // Spawn cleanup task on startup
tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(300));
loop {
interval.tick().await;
cleanup_expired_sessions(&oauth_session_store).await;
}
});3. No Rate Limiting on OAuth EndpointsLocation: OAuth endpoint handlers ( Issue: No rate limiting allows:
Impact: Service availability and security vulnerability. Recommendation: Add rate limiting middleware using use tower_governor::{governor::GovernorConfigBuilder, GovernorLayer};
let governor_conf = Box::new(
GovernorConfigBuilder::default()
.per_second(5)
.burst_size(10)
.finish()
.unwrap()
);
// Apply to OAuth routes
oauth_router.layer(GovernorLayer { config: governor_conf })
|
| Area | Lines | Coverage | Status |
|---|---|---|---|
| OAuth Flows | 742 | Good | ✅ Device, web, callback, exchange |
| Bearer Auth | 406 | Good | ✅ Valid/invalid tokens, RBAC |
| Authorization | 821 | Good | ✅ Permissions, scopes, roles |
| Session Cleanup | 0 | Missing | ❌ No tests for expiration |
| Rate Limiting | 0 | Missing | ❌ No load tests |
| OIDC Failures | 0 | Partial |
Recommendation: Add integration tests for:
- Session cleanup behavior
- Concurrent request handling
- OIDC provider timeout/failure scenarios
🔐 Security Checklist
| Security Aspect | Status | Notes |
|---|---|---|
| PKCE for OAuth | ✅ | Implemented correctly |
| CSRF Protection | ✅ | State parameter validation |
| Secret Zeroization | Good for bearer tokens, improve for PKCE | |
| Input Validation | Basic validation, needs length limits | |
| Rate Limiting | ❌ | Not implemented |
| Session Cleanup | ❌ | Not implemented |
| HTTPS Enforcement | Not enforced in config | |
| Timing-Safe Comparison | ❌ | Standard == used |
| Token Refresh | ❌ | Not implemented |
📚 Documentation Quality
Excellent:
- ✅ Clear OAuth authentication guide with provider examples
- ✅ Comprehensive RBAC documentation with use cases
- ✅ Updated CLI documentation
- ✅ Working configuration examples
Could Improve:
- Add production deployment checklist (HTTPS, rate limits, monitoring)
- Document session cleanup expectations
- Add security best practices section
- Include OAuth token refresh recommendations
🎯 Recommendations Summary
Before Merge (Critical):
- ✅ Fix hardcoded localhost in OAuth callback
- ✅ Implement session cleanup mechanism
- ✅ Add rate limiting to OAuth endpoints
Post-Merge (High Priority):
- Change fallback service to use viewer role
- Implement timing-safe token comparison
- Add secrecy wrapper to PKCE verifier
Future Enhancements:
- Add input validation with length limits
- Implement OAuth token refresh flow
- Add comprehensive load testing
- Document production security requirements
💬 Final Thoughts
This is an excellent implementation of a complex security feature. The code demonstrates:
- Strong understanding of OAuth2/OIDC standards
- Good security practices (PKCE, CSRF, secret handling)
- Comprehensive testing approach
- Clear documentation
The critical issues are relatively minor fixes that don't undermine the core architecture. Once addressed, this will be a production-ready authentication and authorization system.
Great work on this substantial feature! 🚀
📋 Action Items
- Fix hardcoded localhost in OAuth callback fallback
- Implement background task for session cleanup
- Add rate limiting middleware to OAuth endpoints
- Review fallback service permissions (consider viewer role)
- Add timing-safe comparison for bearer tokens
- Add input validation with max length constraints
- Document production deployment security requirements
Review conducted with automated code analysis and security best practices evaluation.
Running 'cargo run' from the ts-generator directory with working-directory was causing path resolution issues in CI. Now runs from workspace root using explicit package specification. This ensures CARGO_MANIFEST_DIR resolves correctly and files are generated in the expected location for subsequent build steps.
Previously, index.ts was a manually maintained file that was gitignored, which caused CI builds to fail since the file didn't exist in the repository. Now ts-generator programmatically creates index.ts with all type guard functions and re-exports, ensuring it's always available during builds. This resolves the Docker build failure where the frontend couldn't load the generated types due to missing index.ts.
Removed the generated index.ts barrel file in favor of inline type guards directly in store files where needed. This eliminates the need to generate and maintain a large TypeScript file in Rust. Changes: - Remove index.ts generation from ts-generator - Add inline type guards in containerLogsStore and taskOutputStore - Remove $generated path alias from svelte.config.js - Import WebSocketMessage directly from generated files Benefits: - No hardcoded TypeScript in Rust strings - Type guards are co-located with their usage - Simpler build process - Easier to maintain
Replaces standard string equality with constant-time comparison using the subtle crate to prevent timing attacks that could reveal valid bearer tokens through response time measurements. The vulnerable code used simple == comparison which short-circuits on the first character mismatch, allowing attackers to determine correct characters through timing analysis. This fix ensures all token comparisons take the same amount of time regardless of match position. Fixes: #520 Relates to: scotty-39
The login handler was still using standard == comparison for bearer tokens, vulnerable to the same timing attack fixed in auth_core.rs. This completes the timing attack mitigation across all token comparison points in the authentication system. Addresses PR #524 review feedback
…se URL The OAuth callback handler used a hardcoded localhost URL for redirecting users back to the frontend after authentication. This prevented OAuth from working in production environments accessed via different hostnames. Changes: - Added 'frontend_base_url' field to OAuthSettings configuration - Defaults to 'http://localhost:21342' for backward compatibility - Updated OAuth callback handler to use configured URL instead of hardcoded value - Maintains support for per-flow frontend_callback_url override The frontend_base_url can now be configured in the settings file: [api.oauth] frontend_base_url = "https://scotty.example.com" Fixes: #517 Relates to: scotty-36
…-token-comparison fix(security): use constant-time comparison for bearer token validation
…se URL The OAuth callback handler used a hardcoded localhost URL for redirecting users back to the frontend after authentication. This prevented OAuth from working in production environments accessed via different hostnames. Changes: - Added 'frontend_base_url' field to OAuthSettings configuration - Defaults to 'http://localhost:21342' for backward compatibility - Updated OAuth callback handler to use configured URL instead of hardcoded value - Maintains support for per-flow frontend_callback_url override The frontend_base_url can now be configured in the settings file: [api.oauth] frontend_base_url = "https://scotty.example.com" Fixes: #517 Relates to: scotty-36
Code Review - PR #467 (v0.2.0)Thank you for this comprehensive PR introducing authentication and authorization to Scotty! This is a significant enhancement that adds OAuth 2.0, bearer token auth, and RBAC via Casbin. The implementation shows solid engineering practices, but there are critical security issues that must be addressed before merging. 🔴 Critical Issues (Must Fix)1. Plaintext Secrets in Version ControlFiles: Bearer tokens and OAuth client secrets are stored as plaintext in configuration files: bearer_tokens:
admin: "gFW5k1fdvYw8iB2xCxw5qZXj5pkP9dga"
client-a: "j3Xq973L67JVAsQpU4PfZAMKdMsfdXsu"Risk: Anyone with repository access can see these secrets. Recommendation:
2. No Token Expiration MechanismFiles: Throughout auth implementation Bearer tokens have no expiration - once issued, they're valid indefinitely. Risk: Stolen tokens can be used perpetually. Recommendation:
3. Insecure Authorization FallbackFile: If Casbin config fails to load, the system falls back to a permissive default: Err(e) => {
warn!("Failed to load authorization config...");
FallbackService::create_fallback_service(settings.api.access_token.clone()).await
}Risk: Configuration errors could grant unintended access. Recommendation: Fail closed - refuse to start if authorization can't be loaded. 4. Container Running as RootFile: User switching is commented out: # RUN chown -R $APP_USER:$APP_USER ${APP}
# USER $APP_USERRisk: Container breakout vulnerabilities. Recommendation: Uncomment and fix permission issues properly, never run as root. 🟡 High Priority Issues (Should Fix)5. Missing Input Validation on Admin EndpointsFiles: Limited validation on user IDs, role names, scope names could allow injection attacks or invalid data. Recommendation: Add comprehensive input validation: fn validate_identifier(s: &str) -> Result<(), &'static str> {
if s.is_empty() || s.len() > 128 {
return Err("Invalid length");
}
if !s.chars().all(|c| c.is_alphanumeric() || c == '-' || c == '_') {
return Err("Invalid characters");
}
Ok(())
}6. In-Memory Session Storage Without CleanupFile: OAuth sessions stored in Risk: Memory leak over time. Recommendation:
7. Limited Security Test CoverageFiles: Test files throughout Found basic tests but missing critical security scenarios:
Recommendation: Add comprehensive security test suite. 8. Missing Security Scanning in CIFile: CI runs tests, fmt, clippy but no security scans. Recommendation: Add: - name: Security audit
run: cargo audit
- name: Dependency checks
run: cargo deny check🟢 Positive ObservationsExcellent work on several security aspects:
📊 Additional RecommendationsMedium Priority:
Low Priority:
🎯 Overall AssessmentRisk Level: HIGH This PR implements valuable functionality with generally good code quality, but critical security issues prevent merging in current state. The secrets management and token expiration issues are particularly concerning for production use. Recommendation: Address all critical issues before merge. The authentication system should be secure by default, with production-ready secret management. 📝 Testing RecommendationsBefore merging, ensure:
Overall: Great foundation! The architecture is solid and the OAuth implementation is well-structured. Once the security issues are addressed, this will be a strong addition to Scotty. Let me know if you'd like me to elaborate on any of these points or provide code examples for the fixes. |
Replaces standard string equality with constant-time comparison using the subtle crate to prevent timing attacks that could reveal valid bearer tokens through response time measurements. The vulnerable code used simple == comparison which short-circuits on the first character mismatch, allowing attackers to determine correct characters through timing analysis. This fix ensures all token comparisons take the same amount of time regardless of match position. Fixes: #520 Relates to: scotty-39
The login handler was still using standard == comparison for bearer tokens, vulnerable to the same timing attack fixed in auth_core.rs. This completes the timing attack mitigation across all token comparison points in the authentication system. Addresses PR #524 review feedback
Add documentation for the new frontend_base_url configuration option that allows OAuth to work with production domains instead of being hardcoded to localhost. Changes: - Add frontend_base_url to OAuth configuration examples - Add SCOTTY__API__OAUTH__FRONTEND_BASE_URL environment variable - Add "Understanding OAuth URLs" section explaining the difference between redirect_url (backend callback) and frontend_base_url (frontend application base) - Include production configuration example - Update environment variables reference table This documents the fix for scotty-36 where OAuth callback URLs were hardcoded to localhost, preventing OAuth from working in production environments.
Code Review: Version 0.2.0This is a major release introducing OAuth authentication, RBAC authorization, unified output system, and observability. This is a comprehensive review with specific findings. 🎯 Executive SummaryOverall Assessment: ✅ APPROVE with Important Recommendations This is a well-architected and substantial release that significantly enhances Scotty's security and operational capabilities. The implementation demonstrates solid engineering practices, good separation of concerns, and comprehensive testing (777 test occurrences across 57 files). However, there are critical security concerns that should be addressed before production deployment. Changed: 245 files, +30,318 additions, -2,253 deletions 🔒 Critical Security Issues1. 🔴 CRITICAL: Hardcoded Bearer Tokens in Version ControlLocation: bearer_tokens:
admin: "gFW5k1fdvYw8iB2xCxw5qZXj5pkP9dga"
client-a: "j3Xq973L67JVAsQpU4PfZAMKdMsfdXsu"
client-b: "mPFMijuZeEAaus94hWApEHzD8JMhrcRk"
hello-world: "mv7UuZddtVpAM5c6tWCFejCp1vizuSlX"
test-bearer-token-123: "9oWZsLsUzFvC01nPqB6nzZpDK6Zlv3KR"Issue: While these appear to be example tokens, committing ANY tokens to version control is a critical security vulnerability. These could be accidentally deployed to production or leaked via forks. Recommendation:
Impact: HIGH - If these are real tokens or get reused, they provide unauthorized access 2. 🟡 MEDIUM: Wildcard User AuthorizationLocation: '*':
- role: viewer
scopes:
- defaultIssue: The wildcard Recommendation:
Impact: MEDIUM - Potential unauthorized read access if auth is misconfigured 3. 🟡 MEDIUM: Token Storage in sessionStorageLocation: Positive: Unlike previous reviews suggested, this implementation uses sessionStorage (not localStorage), which is significantly more secure:
Remaining Concerns:
Recommendations:
Impact: MEDIUM - Acceptable for many use cases, but requires XSS prevention 🐛 Code Quality Issues4. 🟡 MEDIUM: Panic-Prone Code with Multiple
|
The login handler was still using standard == comparison for bearer tokens, vulnerable to the same timing attack fixed in auth_core.rs. This completes the timing attack mitigation across all token comparison points in the authentication system. Addresses PR #524 review feedback
…se URL The OAuth callback handler used a hardcoded localhost URL for redirecting users back to the frontend after authentication. This prevented OAuth from working in production environments accessed via different hostnames. Changes: - Added 'frontend_base_url' field to OAuthSettings configuration - Defaults to 'http://localhost:21342' for backward compatibility - Updated OAuth callback handler to use configured URL instead of hardcoded value - Maintains support for per-flow frontend_callback_url override The frontend_base_url can now be configured in the settings file: [api.oauth] frontend_base_url = "https://scotty.example.com" Fixes: #517 Relates to: scotty-36
…k-url fix(oauth): replace hardcoded localhost with configurable frontend base URL
Code Review: PR #467 - Version 0.2.0OverviewThis is a substantial release introducing major new features across authentication, authorization, observability, and frontend improvements. The PR includes:
Overall AssessmentStatus: ✅ APPROVE WITH RECOMMENDATIONS This PR demonstrates high-quality engineering with well-architected systems. The authorization implementation is particularly impressive, and the observability stack is production-ready. However, there are critical security issues that should be addressed before production deployment. 🎯 Major Features Review1. Authorization System (Casbin RBAC)Strengths:
Code Quality:
2. OAuth/OIDC AuthenticationStrengths:
3. Observability StackStrengths:
Metrics Coverage:
This is production-ready and well-documented. 4. Frontend ImprovementsStrengths:
Code Quality:
🔒 Security AnalysisCritical Issues (Must Fix Before Production)
High Priority
Medium Priority
Positive Security Findings ✅
📊 Code Quality AssessmentRust BackendStrengths:
Minor Issues:
Frontend (TypeScript/Svelte)Strengths:
Suggestions:
🧪 Testing & QualityObservations:
Recommendations:
📝 DocumentationStrengths:
Suggestions:
🚀 Performance ConsiderationsPositive:
Potential Concerns:
Recommendations:
🔄 Breaking Changes
These are well-documented in the migration sections. 📋 Recommendations SummaryBefore Merging
Before Production Deployment
Next Release (0.2.1)
🎉 HighlightsWhat's Really Well Done:
Final RecommendationAPPROVE with conditions: This is high-quality work that significantly enhances Scotty's capabilities. The authorization system is well-architected, and the observability stack is production-ready. However, the OAuth/OIDC implementation has critical security gaps that must be addressed. Recommendation:
Confidence Level: High - The foundations are solid, but security hardening is essential. Detailed Issue TrackerWould you like me to create separate GitHub issues for each security concern? I can draft them with:
Great work on this release! The overall direction is excellent. 🚀 Review generated by Claude Code based on PR diff, documentation, and codebase analysis. |
Add automated session cleanup to prevent memory leaks and implement full observability for OAuth authentication flows. Session Cleanup (scotty-37): - Add periodic cleanup task running every 5 minutes via clokwerk scheduler - Create generic ExpirableSession trait for all session types - Clean DeviceFlowStore, WebFlowStore, and OAuthSessionStore - Prevent unbounded memory growth and DoS attacks - Include comprehensive unit tests for cleanup functionality Metrics Implementation (scotty-41): - Add 10 new OAuth metrics to instruments.rs: * Session gauges: device_flow, web_flow, oauth sessions (sampled every 30s) * Flow counters: device flows, web flows, flow failures * Token validation: total, duration histogram, failures * Cleanup counter: expired sessions cleaned - Create dedicated oauth/metrics.rs module with clean API - Instrument device_flow.rs and handlers.rs with minimal code changes - Record token validation timing and success/failure rates Grafana Dashboard: - Add "OAuth Authentication" section with 9 panels - Session monitoring: 4 gauge panels for active sessions - Flow rates: device/web flow rates with failure tracking - Performance: token validation duration (p50/p95/p99) - Error rates: validation and flow error percentages - Color-coded thresholds and stacked time series Benefits: - Fixes memory leak vulnerability (sessions never expired) - Prevents DoS attacks via session exhaustion - Full observability for authentication health and performance - Enables proactive monitoring and alerting - Minimal impact on business logic (single-line instrumentation) Relates to: scotty-37, scotty-41
…and-metrics feat(oauth): implement session cleanup and comprehensive monitoring
PR Review: Version 0.2.0OverviewThis is a substantial release adding three major features: OAuth authentication, RBAC authorization with Casbin, and observability with OpenTelemetry. The PR includes 31,475 additions across 100+ files. Overall Assessment: The implementation is well-structured with good separation of concerns. However, the PR size makes thorough review challenging. Future releases should consider breaking features into smaller, reviewable chunks. 🎯 Positive HighlightsArchitecture & Design
Security
Code Quality
|
No description provided.